package com.elasticcloudservice.predict;


import com.filetool.util.CloneUtils;

import java.util.*;

public class Crossover {

    private static final Random random = new Random();

    public static Chromosome mate(Chromosome male, Chromosome female, List<ElasticCloudServer> allEcs) {
        Iterator<ElasticCloudServer> mGenes = male.getEcsList().iterator();
        Iterator<ElasticCloudServer> fGenes = female.getEcsList().iterator();
        int randomIndex = random.nextInt(allEcs.size());
        //两个父代交叉得到的后代
        List<ElasticCloudServer> zygote = new ArrayList<>();

        //keeps track of which elements have been passed into zygote
        LinkedHashSet<VirtualMachine> inheritedElements = new LinkedHashSet<>();

        //used to store bins that can be further filled from recessive genes
        ElasticCloudServer recessiveOverflow = CloneUtils.clone(allEcs.get(randomIndex));

        int m = 0;
        while (mGenes.hasNext() && fGenes.hasNext()) {
            final ElasticCloudServer dominant;
            final ElasticCloudServer recessive;
            //every iteration there is a 50% chance that male genes are dominant, 50% chance female is dominant
            if (random.nextBoolean()) {
                dominant = mGenes.next();
                recessive = fGenes.next();
            } else {
                dominant = fGenes.next();
                recessive = mGenes.next();
            }

            //process the dominant gene first
            ElasticCloudServer newEcs = CloneUtils.clone(allEcs.get(randomIndex));
            for (VirtualMachine vm : dominant.getVirtualMachines()) {
                if (!inheritedElements.contains(vm)) {
                    newEcs.add(vm);
                    inheritedElements.add(vm);
                }
            }
            zygote.add(newEcs);
            //process the recessive gene
            for (VirtualMachine vm : recessive.getVirtualMachines()) {
                //if the element has not been inherited...
                if (!inheritedElements.contains(vm)) {
                    //try to add it to the newBin...
                    if (newEcs.getCPUs() >= vm.getCPUs() && newEcs.getMems() >= vm.getMems()) {
                        newEcs.add(vm);
                        inheritedElements.add(vm);
//                        System.out.println("recessive: 第" + ++m + "个元素" + e.size + "被装进了箱子");
                    }
                    //if it won't fit...
                    else {
                        //place newBin in zygote and set newBin to be recessive overflow
//                        zygote.add(newBin);
                        newEcs = recessiveOverflow;
                        //if e can now fit in newBin, add it
                        if (newEcs.getCPUs() >= vm.getCPUs() && newEcs.getMems() >= vm.getMems()) {
                            newEcs.add(vm);
                            inheritedElements.add(vm);
//                            System.out.println("recessive: 第" + ++m + "个元素" + e.size + "被装进了之前的箱子");
                        }
                        //otherwise place newBin in zygote, and create a new recessive bin, then set that as newBin and add e
                        else {
                            recessiveOverflow = CloneUtils.clone(allEcs.get(randomIndex));
                            zygote.add(newEcs);
                            newEcs = recessiveOverflow;
                            newEcs.add(vm);
                            inheritedElements.add(vm);
//                            System.out.println("recessive： 第" + ++m + "个元素" + e.size + "被装进了新开辟的箱子");
                        }
                    }
                }
            }
        }
        //if recessiveOverflow is not empty, make sure to add it to zygote
        if (!recessiveOverflow.getVmList().isEmpty())
            zygote.add(recessiveOverflow);

        Iterator iterator = zygote.iterator();
        while (iterator.hasNext()) {
            if (((ElasticCloudServer) iterator.next()).getVmList().size() == 0)
                iterator.remove();
        }
        return new Chromosome(zygote);
    }

    public static void mutate(Chromosome chromosome, List<ElasticCloudServer> allEcs) {
        List<ElasticCloudServer> ecsList = chromosome.getEcsList();

        //随机生成要被移除的基因
        int srcBinIndex = random.nextInt(ecsList.size());
        int srcElementIndex = random.nextInt(ecsList.get(srcBinIndex).getVmList().size());
        VirtualMachine mutationVm = ecsList.get(srcBinIndex).getVirtualMachines().get(srcElementIndex);
        ecsList.get(srcBinIndex).remove(mutationVm);

        for (int i = 0; i < ecsList.size(); i++) {
            //skip if we are looking in the srcBin
            if (i == srcBinIndex) continue;

            ElasticCloudServer ecs = ecsList.get(i);

            //if bin i can hold mutationElem, then place the elem in bin i and return the new Solution
            if (ecs.getCPUs() >= mutationVm.getCPUs() && ecs.getMems() >= mutationVm.getMems()) {
                ecs.add(mutationVm);
                return;
            }
        }

        //if the method has not returned, then make a new bin and add the elem
        ElasticCloudServer ecs = CloneUtils.clone(allEcs.get(random.nextInt(allEcs.size())));
        ecs.add(mutationVm);

        //add new bin to bins
        ecsList.add(ecs);
    }

}
